昨天我們從零散 ID
升級到各個宣告(global/class/...)的 零散 ID
。
於是我們可在仿照 昨天
,一層 visitor
解決不了。
繼續在中間加一層 Visitor
,近一步搜集 closure。
我們希望他能夠依造他所在的階層,蒐集只有出現該階層的 ID
fileprivate final class IdentifierVisitor: SyntaxVisitor {
lazy var ids: [IdentifierExprSyntax] = []
override final func visit(_ node: ClassDeclSyntax) -> SyntaxVisitorContinueKind {
return .skipChildren
}
override func visit(_ node: ClosureExprSyntax) -> SyntaxVisitorContinueKind {
return .skipChildren
}
override func visit(_ node: FunctionDeclSyntax) -> SyntaxVisitorContinueKind {
return .skipChildren
}
override final func visit(_ node: IdentifierExprSyntax) -> SyntaxVisitorContinueKind {
ids.append(node)
return .skipChildren
}
}
fileprivate final class ClosureVisitor: SyntaxVisitor {
lazy var idVisitor: IdentifierVisitor = IdentifierVisitor(viewMode: .sourceAccurate)
private lazy var subVisitors: [ClosureVisitor] = []
override final func visit(_ node: ClassDeclSyntax) -> SyntaxVisitorContinueKind {
return .skipChildren
}
override func visit(_ node: ClosureExprSyntax) -> SyntaxVisitorContinueKind {
append(node)
return .skipChildren
}
override func visit(_ node: FunctionDeclSyntax) -> SyntaxVisitorContinueKind {
append(node)
return .skipChildren
}
public final func append(_ node: ClosureExprSyntax) {
let visitor = ClosureVisitor(viewMode: .sourceAccurate)
self.subVisitors.append(visitor)
if let sig = node.signature {
super.walk(sig)
self.idVisitor.walk(sig)
}
visitor.walk(node.statements)
self.idVisitor.walk(node.statements)
}
private final func append(_ node: FunctionDeclSyntax) {
let visitor = ClosureVisitor(viewMode: .sourceAccurate)
self.subVisitors.append(visitor)
if let body = node.body {
visitor.walk(body)
idVisitor.walk(body)
}
}
var all: [ClosureVisitor] {
return [self] + subVisitors.flatMap(\.all)
}
}
fileprivate final class DeclVisitor: SyntaxVisitor {
lazy var closureVisitor = ClosureVisitor(viewMode: .sourceAccurate)
private lazy var subVisitors: [DeclVisitor] = []
override func visit(_ node: ClassDeclSyntax) -> SyntaxVisitorContinueKind {
append(node.members)
return .skipChildren
}
public final func customWalk<SyntaxType>(_ node: SyntaxType) where SyntaxType: SyntaxProtocol {
super.walk(node)
self.closureVisitor.walk(node)
self.closureVisitor.idVisitor.walk(node)
}
private final func append<Syntax: SyntaxProtocol>(_ syntax: Syntax) {
let visitor = DeclVisitor(viewMode: .sourceAccurate)
self.subVisitors.append(visitor)
visitor.customWalk(syntax)
}
var all: [DeclVisitor] {
return [self] + subVisitors.flatMap(\.all)
}
var ids: [[String]] {
let result = self.closureVisitor.all.map(\.idVisitor.ids)
return result.map {
$0.map {
$0.withoutTrivia().description
}
}
}
}
private func parse(_ code: String) -> [DeclVisitor] {
let ast = Parser.parse(source: code)
let visitor = DeclVisitor(viewMode: .sourceAccurate)
visitor.customWalk(ast)
return visitor.all
}
import Foundation
import SwiftSyntax
import SwiftParser
import XCTest
final class ClosureTests: XCTestCase {
func testCode() {
let code = """
import Foundation
let global = Out()
class Out {
func test() {
print(global)
}
}
func test() {
class In {
let a = 1
func test(a: String) {
let inner = In()
print(self.a, a, 123)
func test2() {
escape {
nonescape {
self.test(a: "leak")
print(inner)
}
}
}
}
}
}
func escape(block: @escaping () -> Void) {}
func nonescape(block: () -> Void) {}
"""
let expect = [
/// global
[
// let global = Out()
["Out"],
/// func test() {
[],
/// func escape(block: @escaping () -> Void) {}
[],
/// func nonescape(block: () -> Void) {}
[],
],
/// Out
[
/// print(global)
["print", "global",],
[],
],
/// In
[
/// func test(a: String) {
[
/// let inner = In()
"In",
/// print(self.a, a, 123)
"print", "self", "a",
],
/// func test2() {
["escape"],
/// escape {
["nonescape"],
/// nonescape {
[
/// self.test(a: "leak")
"self",
/// print(inner)
"print", "inner",
],
[],
]
]
let result = parse(code).map(\.ids)
XCTAssertEqual(result, expect)
}
}
因為昨天實作的 DeclVisitor
的緣故。
我們可以集中聚焦於 Decl(global/class/...) 的內容
再透過今天的 ClosureVisitor
。
我們可以關注到 closure
的階層關係。